package com.kun.framework.image;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.kun.framework.core.exception.Assert;
import com.kun.framework.core.exception.BizException;
import com.kun.framework.core.util.ComIoUtil;
import com.kun.framework.image.enums.ImageTypeEnum;
import com.luciad.imageio.webp.WebPReadParam;
import com.luciad.imageio.webp.WebPWriteParam;
import javafx.scene.image.Image;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.List;

/**
 * 图片文件工具类
 *
 * @author gzc
 * @since 2024/3/11
 **/
@SuppressWarnings("all")
public class ImageUtil {

    /**
     * 获取图片格式
     *
     * @param imgFile 图片文件对象
     * @return 图片格式名称
     */
    public static String getImgType(File imgFile) {
        // 获取文件类型
        String fileType = FileTypeUtil.getType(imgFile);
        if (StrUtil.isBlank(fileType)) {
            String fileNameAndType = imgFile.getName();
            int indexOf = fileNameAndType.lastIndexOf(".");
            if (indexOf == -1 || indexOf == fileNameAndType.length()) {
                throw new BizException(StrUtil.format("文件名称->【{}】无法从文件流和文件名称中获取文件类型", fileNameAndType));
            }
            fileType = fileNameAndType.substring(indexOf + 1);
            if (StrUtil.isBlank(fileType)) {
                throw new BizException(StrUtil.format("文件名称->【{}】无法从文件流和文件名称中获取文件类型", fileNameAndType));
            }
        }
        return fileType;
    }

    /**
     * 获取图片格式
     *
     * @param imgFilePath 图片文件绝对路径
     * @return 图片格式名称
     */
    public static String getImgType(String imgFilePath) {
        return getImgType(FileUtil.file(imgFilePath));
    }

    public static boolean isImgByPath(String imgFilePath) {
        return ImageTypeEnum.isImg(getImgTypeByPath(imgFilePath));
    }

    public static String getImgTypeByPath(String imgFilePath) {
        if (StrUtil.isNotBlank(imgFilePath) && FileUtil.isAbsolutePath(imgFilePath)) {
            int lastIndexOf = imgFilePath.lastIndexOf(StrPool.DOT);
            if (lastIndexOf == -1 || lastIndexOf == imgFilePath.length()) {
                return "";
            }
            String sub = imgFilePath.substring(lastIndexOf + 1);
            return ImageTypeEnum.contain(sub);
        }
        return "";
    }

    /**
     * 获取图片名称(不包含图片格式)
     *
     * @param imgFilePath 图片文件绝对路径
     * @return 图片名称
     */
    public static String getImgName(String imgFilePath) {
        String imgNameAndType = FileUtil.getName(imgFilePath);
        int indexOf = imgNameAndType.lastIndexOf(StrPool.DOT);
        if (indexOf == -1) {
            return imgNameAndType;
        }
        return imgNameAndType.substring(0, indexOf);
    }

    /**
     * 替换图片文件路径的文件类型
     *
     * @param imgFilePath   图片文件路径
     * @param imageTypeEnum 替换图片类型枚举对象
     * @return 替换指定图片类型后的文件路径
     */
    public static String replaceFileType(String imgFilePath, ImageTypeEnum imageTypeEnum) {
        checkFilePath(imgFilePath);
        if (imageTypeEnum == null) {
            throw new BizException("图片类型枚举为空");
        }
        return FileUtil.getParent(imgFilePath, 1) + File.separator + getImgName(imgFilePath) + StrPool.DOT + imageTypeEnum.name().toLowerCase();
    }

    /**
     * 获取图片名称（名称+格式）
     *
     * @param imgFilePath 图片名称绝对路径
     * @return 图片完整名称
     */
    public static String getImgNameAndType(String imgFilePath) {
        return StrUtil.isNotBlank(imgFilePath) ? FileUtil.getName(imgFilePath) : "";
    }

    /**
     * 根据图片名称排序
     *
     * @param imgFile1 图片1
     * @param imgFile2 图片2
     * @return 对比结果
     */
    public static int sortedFileName(File imgFile1, File imgFile2) {
        int endIndex1 = imgFile1.getName().lastIndexOf(".");
        if (endIndex1 == -1) {
            return 0;
        }
        String sub1 = imgFile1.getName().substring(0, endIndex1);
        int endIndex2 = imgFile2.getName().lastIndexOf(".");
        if (endIndex2 == -1) {
            return 0;
        }
        String sub2 = imgFile2.getName().substring(0, endIndex2);
        if (!NumberUtil.isNumber(sub1) && NumberUtil.isNumber(sub2)) {
            return 1;
        }
        if (NumberUtil.isNumber(sub1) && !NumberUtil.isNumber(sub2)) {
            return -1;
        }
        if (!NumberUtil.isNumber(sub1) && !NumberUtil.isNumber(sub2)) {
            return 1;
        }
        return Long.valueOf(sub1).compareTo(Long.valueOf(sub2));
    }

    /**
     * 读取图片数据
     *
     * @param imgFilePath 图片文件绝对路径
     * @return Image对象
     */
    public static Image readImg(String imgFilePath) throws IOException {
        String type = FileTypeUtil.getTypeByPath(imgFilePath);
        InputStream inputStream = ImageTypeEnum.WEBP.name().equalsIgnoreCase(type) ? ImageUtil.readWebp(imgFilePath) : FileUtil.getInputStream(imgFilePath);
        return new Image(inputStream, 0, 0, true, true);
    }

    /**
     * 读取图片数据
     *
     * @param imgFilePath 图片文件绝对路径
     * @return Image对象
     */
    public static Image readImg(File imgFile) throws IOException {
        return readImg(imgFile.getAbsolutePath());
    }

    /**
     * 获取索引值
     *
     * @param imgFilePathList 图片路径列表
     * @param imgFilePath     搜索的图片路径
     * @return 索引值
     */
    public static int getIndexByList(List<String> imgFilePathList, String imgFilePath) {
        if (CollUtil.isEmpty(imgFilePathList)) {
            throw new BizException("图片路径列表为空");
        }
        if (StrUtil.isBlank(imgFilePath)) {
            throw new BizException("图片文件路径为空");
        }
        int indexOf = imgFilePathList.indexOf(imgFilePath);
        if (indexOf == -1) {
            throw new BizException("当前图片不在图片列表中");
        }
        return indexOf;
    }

    /**
     * 读取webp格式图片
     *
     * @param inputFilePath 图片文件绝对路径
     * @return 图片输入流
     */
    public static InputStream readWebp(String inputFilePath) throws IOException {
        ImageReader reader = ImageIO.getImageReadersByMIMEType("com/kun/framework/core/image/webp").next();
        WebPReadParam readParam = new WebPReadParam();
        readParam.setBypassFiltering(true);
        reader.setInput(new FileImageInputStream(FileUtil.file(inputFilePath)));
        // 解压,转化为png格式
        BufferedImage bufferedImage = reader.read(0, readParam);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ImageIO.write(bufferedImage, ImageTypeEnum.PNG.name().toLowerCase(), outputStream);
        return new ByteArrayInputStream(outputStream.toByteArray());
    }

    /**
     * 转换成webp图片文件
     *
     * @param inputFilePath  源图片文件绝对路径
     * @param outputFilePath 输出webp文件绝对路径
     * @return webp文件对象
     */
    public static File toWebpFile(String inputFilePath, String outputFilePath) throws Exception {
        checkFilePath(inputFilePath);
        return toWebpFile(FileUtil.readBytes(inputFilePath), outputFilePath);
    }

    /**
     * 转换成webp图片文件
     *
     * @param inputBytes     源图片文件二进制数组
     * @param outputFilePath 输出webp文件绝对路径
     * @return webp文件对象
     */
    public static File toWebpFile(byte[] inputBytes, String outputFilePath) throws Exception {
        checkFilePath(outputFilePath);
        String fileNameAndType = FileUtil.getName(outputFilePath);
        if (StrUtil.isBlank(fileNameAndType)) {
            throw new BizException(StrUtil.format("文件路径【{}】未获取到文件名称!", outputFilePath));
        }
        String fileName;
        String fileType;
        int indexOf = fileNameAndType.lastIndexOf(StrPool.DOT);
        if (indexOf == -1) {
            fileName = fileNameAndType;
            fileType = ImageTypeEnum.WEBP.toLowerCase();
        } else {
            fileName = fileNameAndType.substring(0, indexOf);
            fileType = ImageTypeEnum.WEBP.toLowerCase();
        }
        String newFileNameAndType = fileName + StrPool.DOT + fileType;
        String parentDir = FileUtil.getParent(outputFilePath, 1);
        String newOutputFilePath = parentDir + File.separator + newFileNameAndType;

        byte[] bytes = toWebpBytes(inputBytes);
        if (ArrayUtil.isEmpty(bytes)) {
            throw new BizException("转换成webp二进制流为空");
        }
        return FileUtil.writeBytes(bytes, newOutputFilePath);
    }

    /**
     * 转换成webp图片文件
     *
     * @param inputFilePath 源图片文件绝对路径
     * @return 输出webp图片二进制数组
     */
    public static byte[] toWebpBytes(String inputFilePath) throws Exception {
        return toWebpStream(inputFilePath).toByteArray();
    }

    /**
     * 转换成webp图片文件
     *
     * @param inputFilePath 源图片文件绝对路径
     * @return 输出webp图片字节流
     */
    public static ByteArrayOutputStream toWebpStream(String inputFilePath) throws Exception {
        // 校验参数
        checkFilePath(inputFilePath);
        File file = FileUtil.file(inputFilePath);
        return toWebpStream(file);
    }

    /**
     * 转换成webp图片文件
     *
     * @param inputFile 源图片文件对象
     * @return 输出webp图片二进制数组
     */
    public static byte[] toWebpBytes(File inputFile) throws Exception {
        return toWebpStream(inputFile).toByteArray();
    }

    /**
     * 转换成webp图片文件
     *
     * @param inputFile 源图片文件对象
     * @return 输出webp图片字节流
     */
    public static ByteArrayOutputStream toWebpStream(File inputFile) throws Exception {
        if (inputFile == null) {
            throw new BizException("File为空");
        }
        if (!inputFile.exists()) {
            throw new BizException("文件不存在");
        }
        return toWebpStream(FileUtil.getInputStream(inputFile));
    }

    /**
     * 转换成webp图片文件
     *
     * @param inputBytes 源图片二进制数组
     * @return 输出webp图片二进制数组
     */
    public static byte[] toWebpBytes(byte[] inputBytes) throws Exception {
        ByteArrayOutputStream outputStream = toWebpStream(inputBytes);
        byte[] bytes = outputStream.toByteArray();
        outputStream.close();
        return bytes;
    }

    /**
     * 转换成webp图片文件
     *
     * @param inputBytes 源图片二进制数组
     * @return 输出webp图片字节流
     */
    public static ByteArrayOutputStream toWebpStream(byte[] inputBytes) throws Exception {
        ByteArrayInputStream inputStream = ComIoUtil.getInputStream(inputBytes);
        return toWebpStream(inputStream);
    }

    /**
     * 转换成webp图片文件
     *
     * @param inputStream 源图片输入流
     * @return 输出webp图片二进制数组
     */
    public static byte[] toWebpBytes(InputStream inputStream) throws Exception {
        ByteArrayOutputStream outputStream = toWebpStream(inputStream);
        byte[] bytes = outputStream.toByteArray();
        outputStream.close();
        return bytes;
    }

    /**
     * 转换成webp图片文件
     *
     * @param inputStream 源图片输入流
     * @return 输出webp图片字节流
     */
    public static ByteArrayOutputStream toWebpStream(InputStream inputStream) throws Exception {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024);
        if (inputStream != null) {
            // 读取文件
            IIOImage iioImage = new IIOImage(ImageIO.read(inputStream), null, null);
            ImageWriter writer = ImageIO.getImageWritersByMIMEType("com/kun/framework/core/image/webp").next();
            WebPWriteParam writeParam = new WebPWriteParam(writer.getLocale());
            // 设置压缩模式
            writeParam.setCompressionMode(WebPWriteParam.MODE_EXPLICIT);
            ImageOutputStream imageOutputStream = new MemoryCacheImageOutputStream(outputStream);
            writer.setOutput(imageOutputStream);
            // 编码
            writer.write(iioImage.getMetadata(), iioImage, writeParam);
            writer.dispose();
            // 必须调用close()才能调用flushBefore()往outputStream写入数据，如果不调用close()，outputStream没有数据
            imageOutputStream.close();
        }
        return outputStream;
    }

    /**
     * webp格式转换其他格式
     *
     * @param inputFile     图片文件对象
     * @param outputFile    输出图片文件对象
     * @param imageTypeEnum 图片格式枚举
     * @return 输出图片文件对象
     */
    public static File toOtherFormatFile(File inputFile, File outputFile, ImageTypeEnum imageTypeEnum) throws Exception {
        return toOtherFormatFile(FileUtil.getInputStream(inputFile), outputFile.getAbsolutePath(), imageTypeEnum);
    }

    /**
     * webp格式转换其他格式
     *
     * @param inputBytes     图片二进制数组
     * @param outputFilePath 输出图片文件路径
     * @param imageTypeEnum  图片格式枚举
     * @return 输出图片文件对象
     */
    public static File toOtherFormatFile(byte[] inputBytes, String outputFilePath, ImageTypeEnum imageTypeEnum) throws Exception {
        return toOtherFormatFile(ComIoUtil.getInputStream(inputBytes), outputFilePath, imageTypeEnum);
    }

    /**
     * webp格式转换其他格式
     *
     * @param inputStream    图片输入流
     * @param outputFilePath 输出图片文件路径
     * @param imageTypeEnum  图片格式枚举
     * @return 输出图片文件对象
     */
    public static File toOtherFormatFile(InputStream inputStream, String outputFilePath, ImageTypeEnum imageTypeEnum) throws Exception {
        byte[] bytes = toOtherFormatBytes(inputStream, imageTypeEnum);
        return FileUtil.writeBytes(bytes, FileUtil.touch(replaceFileType(outputFilePath, imageTypeEnum)));
    }

    /**
     * webp格式转换其他格式
     *
     * @param inputBytes    图片输入二进制数组
     * @param imageTypeEnum 图片格式枚举
     * @return 图片二进制数组
     */
    public static byte[] toOtherFormatBytes(byte[] inputBytes, ImageTypeEnum imageTypeEnum) throws Exception {
        ByteArrayOutputStream outputStream = toOtherFormatStream(inputBytes, imageTypeEnum);
        byte[] bytes = outputStream.toByteArray();
        outputStream.close();
        return bytes;
    }

    /**
     * webp格式转换其他格式
     *
     * @param inputBytes    图片输入二进制数组
     * @param imageTypeEnum 图片格式枚举
     * @return 图片二进制流
     */
    public static ByteArrayOutputStream toOtherFormatStream(byte[] inputBytes, ImageTypeEnum imageTypeEnum) throws Exception {
        ByteArrayInputStream inputStream = ComIoUtil.getInputStream(inputBytes);
        return toOtherFormatStream(inputStream, imageTypeEnum);
    }

    /**
     * webp格式转换其他格式
     *
     * @param inputStream   图片输入流
     * @param imageTypeEnum 图片格式枚举
     * @return 图片二进制数组
     */
    public static byte[] toOtherFormatBytes(InputStream inputStream, ImageTypeEnum imageTypeEnum) throws Exception {
        ByteArrayOutputStream outputStream = toOtherFormatStream(inputStream, imageTypeEnum);
        byte[] bytes = outputStream.toByteArray();
        outputStream.close();
        return bytes;
    }

    /**
     * webp格式转换其他格式
     *
     * @param inputStream   图片输入流
     * @param imageTypeEnum 图片格式枚举
     * @return 输出流
     */
    public static ByteArrayOutputStream toOtherFormatStream(InputStream inputStream, ImageTypeEnum imageTypeEnum) throws Exception {
        if (imageTypeEnum == null) {
            throw new BizException("图片格式枚举为空");
        }
        byte[] bytes = ComIoUtil.getBytes(inputStream);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        if (inputStream != null && ArrayUtil.isNotEmpty(bytes)) {
            String fileType = ComIoUtil.getFileType(bytes);
            if (ImageTypeEnum.WEBP.name().equalsIgnoreCase(fileType)) {
                throw new BizException(StrUtil.format("文件类型【{}】不是webp类型", fileType));
            }
            ImageReader reader = ImageIO.getImageReadersByMIMEType("com/kun/framework/core/image/webp").next();
            WebPReadParam readParam = new WebPReadParam();
            readParam.setBypassFiltering(true);
            reader.setInput(new MemoryCacheImageInputStream(ComIoUtil.getInputStream(bytes)));
            // 解压
            ImageIO.write(reader.read(0, readParam), imageTypeEnum.name().toLowerCase(), outputStream);
        }
        return outputStream;
    }

    /**
     * 校验文件路径格式
     *
     * @param filePath 文件路径
     */
    private static void checkFilePath(String filePath) throws BizException {
        Assert.notBlank(filePath, "文件路径为空");
        Assert.isTrue(FileUtil.isAbsolutePath(filePath), "文件路径【{}】格式错误!", filePath);
    }

    /**
     * 校验文件对象
     *
     * @param file 文件对象
     */
    private static void checkFile(File file) {
        Assert.notNull(file, "文件对象为空");
        Assert.isTrue(file.exists(), "文件对象不存在");
    }

}
